package com._119yn.host.gst200.service.impl;

import com._119yn.host.gst200.cansocket.message.CanFrame;
import com._119yn.host.gst200.constant.HostStatuses;
import com._119yn.host.gst200.constant.LogTags;
import com._119yn.host.gst200.constant.SysAttrs;
import com._119yn.host.gst200.database.dao.DeviceStatusDao;
import com._119yn.host.gst200.database.dao.HostSessionDao;
import com._119yn.host.gst200.database.entity.DeviceStatus;
import com._119yn.host.gst200.database.entity.HostSession;
import com._119yn.host.gst200.database.entity.PointTableRecord;
import com._119yn.host.gst200.event.PointTableImportedEvent;
import com._119yn.host.gst200.model.Signal;
import com._119yn.host.gst200.service.CanFrameSender;
import com._119yn.host.gst200.service.HostService;
import com._119yn.host.gst200.service.PointTableService;
import com._119yn.host.gst200.service.SysAttrService;
import lombok.RequiredArgsConstructor;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class HostServiceImpl implements HostService, ApplicationRunner {

    private final HostSessionDao hostSessionDao;

    private final DeviceStatusDao deviceStatusDao;

    private final PointTableService pointTableService;

    private final SysAttrService sysAttrService;

    private CanFrameSender canFrameSender;

    @Synchronized
    @Override
    public void register(CanFrameSender sender) {
        this.canFrameSender = sender;
    }

    @Synchronized
    @Override
    public void run(ApplicationArguments args) throws Exception {

    }

    @Synchronized
    @EventListener
    @Transactional
    public void onPointTableImportedEvent(PointTableImportedEvent event) {
        refreshHostSession();
    }

    @Override
    public void refreshHostSession() {
        List<PointTableRecord> controllerPointTableRecords = pointTableService.getAllControllers();
        Map<Integer, PointTableRecord> map = controllerPointTableRecords.stream().collect(Collectors.toMap(PointTableRecord::getPanel, Function.identity()));

        Set<Integer> panels = new HashSet<>();
        for (PointTableRecord controllerPointTableRecord : controllerPointTableRecords) {
            panels.add(controllerPointTableRecord.getPanel());
        }
        if (panels.isEmpty()) {
            hostSessionDao.truncate();
        } else {
            LocalDateTime now = LocalDateTime.now();
            List<HostSession> hostSessions = hostSessionDao.selectAll();
            // 先处理新增的
            for (int panel : panels) {
                boolean needAdded = true;
                boolean needUpdated = false;
                for (HostSession hostSession : hostSessions) {
                    if (hostSession.getHostId() == panel) {
                        needAdded = false;
                        break;
                    }
                }
                if (needAdded) {
                    PointTableRecord pointTableRecord = map.get(panel);
                    HostSession hostSession = new HostSession();
                    hostSession.setDeviceId(pointTableRecord.getDeviceId());
                    hostSession.setDeviceAddress(pointTableRecord.getDeviceAddress());
                    hostSession.setDeviceType(pointTableRecord.getDeviceType());
                    hostSession.setHostId(panel);
                    hostSession.setSequence(null);
                    hostSession.setStatus(null);
                    hostSession.setCreateTime(now);
                    hostSession.setLaunchTime(now);
                    hostSession.setActiveTime(null);
                    hostSessionDao.insert(hostSession);
                }
            }
            // 再处理删除的
            for (HostSession hostSession : hostSessions) {
                if (!panels.contains(hostSession.getHostId())) {
                    hostSessionDao.deleteByHostId(hostSession.getHostId());
                }
            }
        }
    }

    @Synchronized
    @Scheduled(fixedDelay = 1000)
    public void taskHostOfflineCheck() {
        Integer i = sysAttrService.getInteger(SysAttrs.HOST_TIMEOUT);
        if (i == null) {
            return;
        }
        Duration timeout = Duration.ofSeconds(i);
        List<HostSession> hostSessions = getAllHostSessions();
        for (HostSession hostSession : hostSessions) {
            if (hostSession.getOffline() == null || hostSession.getOffline() != 1) {
                LocalDateTime time;
                if (hostSession.getActiveTime() != null) {
                    time = hostSession.getActiveTime();
                } else {
                    time = hostSession.getLaunchTime();
                }
                int r = Duration.between(time, LocalDateTime.now()).compareTo(timeout);
                if (r > 0) {
                    hostSession.setOffline(1);
                    hostSessionDao.updateByHostId(hostSession);
                }
            }
        }
    }

    @Override
    public List<HostSession> getAllHostSessions() {
        return hostSessionDao.selectAll();
    }

    @Override
    public List<HostSession> getOfflineHostSessions() {
        return hostSessionDao.selectByOffline(1);
    }

    @Override
    public boolean isAllHostSessionOffline() {
        return hostSessionDao.isAllHostSessionOffline();
    }

    @Override
    public DeviceStatus getDeviceStatusByDeviceId(String deviceId, boolean create) {
        DeviceStatus deviceStatus = deviceStatusDao.selectByDeviceId(deviceId);
        if (deviceStatus == null && create) {
            deviceStatus = new DeviceStatus();
            deviceStatus.setDeviceId(deviceId);
            deviceStatusDao.insert(deviceStatus);
        }
        return deviceStatus;
    }

    @Override
    public List<DeviceStatus> getAllDeviceStatus() {
        return deviceStatusDao.selectAll();
    }

    @Override
    public HostSession getHostSessionByHostId(int hostId) {
        return getHostSessionByHostId(hostId, false);
    }

    @Override
    public HostSession getHostSessionByHostId(int hostId, boolean create) {
        HostSession hostSession = hostSessionDao.selectByHostId(hostId);
        if (hostSession == null) {
            if (create) {
                LocalDateTime now = LocalDateTime.now();
                hostSession = new HostSession();
                hostSession.setHostId(hostId);
                hostSession.setSequence(null);
                hostSession.setStatus(null);
                hostSession.setCreateTime(now);
                hostSession.setLaunchTime(now);
                hostSession.setActiveTime(null);
                hostSessionDao.insert(hostSession);
            }
        }
        return hostSession;
    }

    @Override
    public HostSession getHostSessionByDeviceId(String deviceId) {
        PointTableRecord pointTableRecord = pointTableService.getByDeviceId(deviceId);
        if (pointTableRecord == null) {
            return null;
        }
        return getHostSessionByHostId(pointTableRecord.getPanel(), false);
    }

    @Override
    public HostSession getHostSessionByDeviceId(String deviceId, boolean create) {
        PointTableRecord pointTableRecord = pointTableService.getByDeviceId(deviceId);
        if (pointTableRecord == null) {
            return null;
        }
        return getHostSessionByHostId(pointTableRecord.getPanel(), create);
    }

    @Override
    public void saveHostSessionByHostId(HostSession hostSession) {
        hostSessionDao.updateByHostId(hostSession);
    }

    @Synchronized
    @Override
    public void hostSilence(int host) {
        sendHostSilence(getHostSessionByHostId(host));
    }

    @Synchronized
    @Override
    public void hostReset(int host) {
        sendHostReset(getHostSessionByHostId(host));
    }

    @Synchronized
    @Override
    public void hostManualForbid(int host) {
        sendHostManualForbid(getHostSessionByHostId(host));
    }

    @Synchronized
    @Override
    public void hostManualPermit(int host) {
        sendHostManualPermit(getHostSessionByHostId(host));
    }

    @Synchronized
    @Override
    public void hostAutoForbid(int host) {
        sendHostAutoForbid(getHostSessionByHostId(host));
    }

    @Synchronized
    @Override
    public void hostAutoPartial(int host) {
        sendHostAutoPartial(getHostSessionByHostId(host));
    }

    @Synchronized
    @Override
    public void hostAutoFull(int host) {
        sendHostAutoFull(getHostSessionByHostId(host));
    }

    @Synchronized
    @Override
    public void hostSprayForbid(int host) {
        sendHostSprayForbid(getHostSessionByHostId(host));
    }

    @Synchronized
    @Override
    public void hostSprayPermit(int host) {
        sendHostSprayPermit(getHostSessionByHostId(host));
    }

    @Synchronized
    @Override
    public void hostGuardDisable(int host) {
        sendHostGuardDisable(getHostSessionByHostId(host));
    }

    @Synchronized
    @Override
    public void hostGuardEnable(int host) {
        sendHostGuardEnable(getHostSessionByHostId(host));
    }

    @Synchronized
    @Override
    public void deviceStartup(int host, int devCode, int devType) {
        sendDeviceStartup(getHostSessionByHostId(host), devCode, devType);
    }

    @Synchronized
    @Override
    public void deviceStartupStop(int host, int devCode, int devType) {
        sendDeviceStartupStop(getHostSessionByHostId(host), devCode, devType);
    }

    @Synchronized
    @Override
    public void deviceShield(int host, int devCode, int devType) {
        sendDeviceShield(getHostSessionByHostId(host), devCode, devType);
    }

    @Synchronized
    @Override
    public void deviceShieldCancel(int host, int devCode, int devType) {
        sendDeviceShieldCancel(getHostSessionByHostId(host), devCode, devType);
    }

    @Override
    public void active(HostSession hostSession, Signal signal) {
        hostSession.setActiveTime(LocalDateTime.now());
        hostSession.setOffline(0);
        hostSession.setSequence(signal.getSequence());
        saveHostSessionByHostId(hostSession);
    }

    @Override
    public void deviceShield(String deviceId) {
        PointTableRecord pointTableRecord = pointTableService.getByDeviceId(deviceId);
        if (pointTableRecord == null) {
            return;
        }
        deviceShield(pointTableRecord.getPanel(), Integer.parseInt(pointTableRecord.getDeviceAddress()), Integer.parseInt(pointTableRecord.getDeviceType()));
    }

    @Override
    public void deviceShieldCancel(String deviceId) {
        PointTableRecord pointTableRecord = pointTableService.getByDeviceId(deviceId);
        if (pointTableRecord == null) {
            return;
        }
        deviceShieldCancel(pointTableRecord.getPanel(), Integer.parseInt(pointTableRecord.getDeviceAddress()), Integer.parseInt(pointTableRecord.getDeviceType()));
    }

    @Override
    public void deviceStartup(String deviceId) {
        PointTableRecord pointTableRecord = pointTableService.getByDeviceId(deviceId);
        if (pointTableRecord == null) {
            return;
        }
        deviceStartup(pointTableRecord.getPanel(), Integer.parseInt(pointTableRecord.getDeviceAddress()), Integer.parseInt(pointTableRecord.getDeviceType()));
    }

    @Override
    public void deviceStartupStop(String deviceId) {
        PointTableRecord pointTableRecord = pointTableService.getByDeviceId(deviceId);
        if (pointTableRecord == null) {
            return;
        }
        deviceStartupStop(pointTableRecord.getPanel(), Integer.parseInt(pointTableRecord.getDeviceAddress()), Integer.parseInt(pointTableRecord.getDeviceType()));
    }

    @Override
    public void saveDeviceStatus(DeviceStatus deviceStatus) {
        DeviceStatus status = deviceStatusDao.selectByDeviceId(deviceStatus.getDeviceId());
        if (status == null) {
            deviceStatusDao.insert(deviceStatus);
        } else {
            deviceStatusDao.updateByDeviceId(deviceStatus);
        }
    }

    @Override
    public void changeOnlineHostToSilence(Integer value) {
        hostSessionDao.updateSilenceAndActiveTime(value, LocalDateTime.now());
    }

    @Override
    public void changeOnlineHostStatuses(Long status, Integer manualSta, Integer autoSta, Integer spraySta, Integer guardSta) {
        hostSessionDao.updateStatusesAndActiveTime(status, manualSta, autoSta, spraySta, guardSta, LocalDateTime.now());
    }

    @Override
    public void cleanDeviceStatus() {
        deviceStatusDao.truncate();
    }

    @Override
    public List<HostSession> getOnlineHostSessions() {
        return hostSessionDao.selectByOffline(0);
    }

    /**
     * 发送主机消音信号
     */
    @Synchronized
    private void sendHostSilence(HostSession session) {
        int code = 0x26, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createZeroCanData();
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createZeroCanData();
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    /**
     * 发送主机复位信号
     */
    @Synchronized
    private void sendHostReset(HostSession session) {
        int code = 0x25, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createZeroCanData();
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createZeroCanData();
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendHostManualPermit(HostSession session) {
        int code = 0x27, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        Long status = getHostStatus(session);
        if (status == null) {
            logHostStatusUnsynchronized(session);
            return;
        }
        long newStatus = status & HostStatuses.MANUAL_REVERSE_UNMASK | HostStatuses.MANUAL_PERMIT;

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendHostManualForbid(HostSession session) {
        int code = 0x27, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        Long status = getHostStatus(session);
        if (status == null) {
            logHostStatusUnsynchronized(session);
            return;
        }
        long newStatus = status & HostStatuses.MANUAL_REVERSE_UNMASK | HostStatuses.MANUAL_FORBID;

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendHostAutoFull(HostSession session) {
        int code = 0x27, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        Long status = getHostStatus(session);
        if (status == null) {
            logHostStatusUnsynchronized(session);
            return;
        }
        long newStatus = status & HostStatuses.AUTO_REVERSE_UNMASK | HostStatuses.AUTO_FULL;

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendHostAutoPartial(HostSession session) {
        int code = 0x27, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        Long status = getHostStatus(session);
        if (status == null) {
            logHostStatusUnsynchronized(session);
            return;
        }
        long newStatus = status & HostStatuses.AUTO_REVERSE_UNMASK | HostStatuses.AUTO_PARTIAL;

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendHostAutoForbid(HostSession session) {
        int code = 0x27, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        Long status = getHostStatus(session);
        if (status == null) {
            logHostStatusUnsynchronized(session);
            return;
        }
        long newStatus = status & HostStatuses.AUTO_REVERSE_UNMASK | HostStatuses.AUTO_FORBID;

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendHostSprayPermit(HostSession session) {
        int code = 0x27, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        Long status = getHostStatus(session);
        if (status == null) {
            logHostStatusUnsynchronized(session);
            return;
        }
        long newStatus = status & HostStatuses.AUTO_REVERSE_UNMASK | HostStatuses.SPRAY_PERMIT;

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendHostSprayForbid(HostSession session) {
        int code = 0x27, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        Long status = getHostStatus(session);
        if (status == null) {
            logHostStatusUnsynchronized(session);
            return;
        }
        long newStatus = status & HostStatuses.SPRAY_REVERSE_UNMASK | HostStatuses.SPRAY_FORBID;

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendHostGuardEnable(HostSession session) {
        int code = 0x27, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        Long status = getHostStatus(session);
        if (status == null) {
            logHostStatusUnsynchronized(session);
            return;
        }
        long newStatus = status & HostStatuses.GUARD_REVERSE_MASK | HostStatuses.GUARD_ENABLE;

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendHostGuardDisable(HostSession session) {
        int code = 0x27, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        Long status = getHostStatus(session);
        if (status == null) {
            logHostStatusUnsynchronized(session);
            return;
        }
        long newStatus = status & HostStatuses.GUARD_REVERSE_MASK | HostStatuses.GUARD_DISABLE;

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createHostStatusChangeCanData(newStatus);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendDeviceStartup(HostSession session, int devCode, int devType) {
        int code = 0x28, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createDeviceOperateCanData(devCode, devType);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createDeviceOperateCanData(devCode, devType);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendDeviceStartupStop(HostSession session, int devCode, int devType) {
        int code = 0x29, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createDeviceOperateCanData(devCode, devType);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createDeviceOperateCanData(devCode, devType);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendDeviceShield(HostSession session, int devCode, int devType) {
        int code = 0x2A, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createDeviceOperateCanData(devCode, devType);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createDeviceOperateCanData(devCode, devType);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    @Synchronized
    private void sendDeviceShieldCancel(HostSession session, int devCode, int devType) {
        int code = 0x2B, mark = 0x00, host = getHost(session), sequence = getNextSequence(session);

        List<CanFrame> canFrames = new ArrayList<>(2);

        // Call帧
        {
            byte[] canId = createStandardCanId(0x10, code, mark, host, sequence);
            byte[] canData = createDeviceOperateCanData(devCode, devType);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }
        // Resp帧
        {
            byte[] canId = createStandardCanId(0x00, code, mark, host, sequence);
            byte[] canData = createDeviceOperateCanData(devCode, devType);
            canFrames.add(new CanFrame(createLineData(canId, canData)));
        }

        canFrameSender.send(canFrames);
    }

    /**
     * 获取控制主机编号
     *
     * @param session 主机会话
     * @return 控制主机编号
     */
    private int getHost(HostSession session) {
        if (session == null) {
            return 0;
        } else {
            return session.getHostId();
        }
    }

    /**
     * 获取下一个主机会话的序列号
     *
     * @param session 主机会话
     * @return 下一个主机会话的序列号
     */
    private int getNextSequence(HostSession session) {
        if (session.getSequence() == null) {
            return 0;
        }
        session.setSequence(session.getSequence() + 1);
        if (session.getSequence() > 255) {
            session.setSequence(1);
        }
        return session.getSequence();
    }

    /**
     * 获取控制主机状态
     *
     * @param session 主机会话
     * @return 控制主机状态
     */
    private Long getHostStatus(HostSession session) {
        return session.getStatus();
    }

    private byte[] createStandardCanId(int flag, int code, int mark, int host, int sequence) {
        // 0001 1110 0010 0000 0000 0001 1110 1011
        byte[] canId = new byte[4];
        canId[0] = (byte) (flag | ((code & 0b01111000) >>> 3));
        canId[1] = (byte) (((code & 0b00000111) << 5) | (mark & 0b00000111));
        canId[2] = (byte) (host & 0xFF);
        canId[3] = (byte) (sequence & 0xFF);
        return canId;
    }

    private byte[] createZeroCanData() {
        return new byte[]{0, 0, 0, 0, 0, 0, 0, 0};
    }

    private byte[] createHostStatusChangeCanData(long status) {
        byte[] canData = new byte[8];
        canData[0] = (byte) (0xFF & (status >>> 8));
        canData[1] = (byte) (0xFF & (status));
        canData[2] = (byte) 0x00;
        canData[3] = (byte) 0x00;
        canData[4] = (byte) 0x0A;
        canData[5] = (byte) 0x00;
        fillChecksum(canData);
        return canData;
    }

    private byte[] createDeviceOperateCanData(int devCode, int devType) {
        List<Byte> devCodes = pointTableService.getStandardDevCode(devCode)
                .chars()
                .mapToObj(String::valueOf)
                .map(Integer::parseInt)
                .map(i -> (byte) (i & 0x0F))
                .toList();
        byte[] canData = new byte[8];
        canData[0] = (byte) ((devCodes.get(0) << 4) | devCodes.get(1));
        canData[1] = (byte) ((devCodes.get(2) << 4) | devCodes.get(3));
        canData[2] = (byte) ((devCodes.get(4) << 4) | devCodes.get(5));
        canData[3] = (byte) (0xFF & (devType));
        canData[4] = (byte) 0x0A;
        canData[5] = (byte) 0x00;
        fillChecksum(canData);
        return canData;
    }

    private void fillChecksum(byte[] canData) {
        short sum = getChecksum8t16(canData, 0, canData.length - 2);
        canData[canData.length - 2] = (byte) (0xFF & sum);
        canData[canData.length - 1] = (byte) (0xFF & (sum >> 8));
    }

    private static short getChecksum8t16(byte[] data) {
        return getChecksum8t16(data, 0, data.length);
    }

    private static short getChecksum8t16(byte[] data, int offset, int length) {
        int sum = 0;
        for (int i = 0; i < length; i++) {
            sum += data[offset + i] & 0xFF;
        }
        return (short) (sum & 0xFFFF);
    }

    private String createLineData(byte[] canId, byte[] canData) {
        StringBuilder sb = new StringBuilder();
        for (byte b : canId) {
            sb.append(String.format("%02X", b));
        }
        sb.append("#");
        for (byte b : canData) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }

    private void logHostStatusUnsynchronized(HostSession session) {
        log.warn("{}:[状态异常]: 主机地址={}，主机类型={}, 请先对主机进行复位操作，待DTU与主机状态同步后才可进行远程状态转换操作", LogTags.getHostServer(), session.getHostId(), session.getDeviceType());
    }

}
